# -*- coding:utf-8 -*-
import json
import os
import re
from typing import List

from fastapi import APIRouter, Request, Form, Query
from pydantic import BaseModel

from config.app import get_config
from core.response import success
from core.storage import MySQLDB

router = APIRouter()


class TableInfo(BaseModel):
    tableName: str = Form(min_length=2)
    tableAlias: str = Form(min_length=2)
    tableTitle: str = Form(min_length=2)
    moduleName: str = Form(default="")
    apiPath: str = Form(min_length=2)
    fields: List = Form(...)


router = APIRouter()

dbOptions = {'host': '127.0.0.1', 'user': 'root', 'pwd': '123123', 'db': "fastframework", 'port': 3307}


def getClassName(name: str):
    return "".join(map(lambda x: x[0].upper() + x[1:].lower(), name.split("_")))


@router.get("/module")
def module(name: str = Query()):
    settings = get_config()
    os.makedirs(settings.APP_PATH + "/" + name, exist_ok=True)
    os.makedirs(settings.APP_PATH + "/" + name + "/controller", exist_ok=True)
    os.makedirs(settings.APP_PATH + "/" + name + "/aspect", exist_ok=True)
    os.makedirs(settings.APP_PATH + "/" + name + "/dao", exist_ok=True)
    os.makedirs(settings.APP_PATH + "/" + name + "/entity", exist_ok=True)
    os.makedirs(settings.APP_PATH + "/" + name + "/service", exist_ok=True)
    os.makedirs(settings.APP_PATH + "/" + name + "/annotation", exist_ok=True)

    with open(settings.APP_PATH + "/" + name + "/router.py", "w", encoding="utf8") as f:
        f.write("# -*- coding:utf-8 -*-\n\n")
        f.write("from fastapi import APIRouter\n\n")
        f.write("api_router = APIRouter(prefix='/api')\n\n")

    return success("OK")


@router.post("/code")
async def code(req: Request, tableInfo: TableInfo, deploy: int = Query(default=0)):
    settings = get_config()
    DBNAME = "fastframework"
    BR: str = "\n"
    SPACE: str = "    "
    SPACE2: str = "  "
    CLASS_NAME = getClassName(tableInfo.tableName)
    PRI: str = ""

    ret = {'files': '', 'Entity': '', 'Dao': '', 'Service': '', 'Controller': '', 'api.js': '', 'index.vue': '',
           'add-or-update.vue': '', "route": "", "__ini__": "", "fields": []}

    module_root = settings.APP_PATH

    moduleName = "" if tableInfo.moduleName == "" else tableInfo.moduleName + "/"
    moduleName2 = "" if tableInfo.moduleName == "" else tableInfo.moduleName + "."

    CONTROLLER_PATH = module_root + "/controller/%s%s_controller.py" % ( moduleName, tableInfo.tableAlias)
    DAO_PATH = module_root + "/dao/%s%s_dao.py" % (moduleName, tableInfo.tableAlias)
    ENTITY_PATH = module_root + "/entity/%s%s.py" % (moduleName,  tableInfo.tableAlias)
    SERVICE_PATH = module_root + "/service/%s%s_service.py" % (moduleName, tableInfo.tableAlias)

    # ~~ route ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ret['route'] += "# " + module_root + BR
    ret['route'] += "# " + CONTROLLER_PATH + BR
    ret['route'] += "# " + DAO_PATH + BR
    ret['route'] += "# " + ENTITY_PATH + BR
    ret['route'] += "# " + SERVICE_PATH + BR

    ret['route'] += BR
    if tableInfo.moduleName == "":
        ret['route'] += 'from app.controller import %s_router %s' % (tableInfo.tableAlias, BR)
    else:
        ret['route'] += 'from app.controller.%s import %s_router %s' % ( tableInfo.moduleName, tableInfo.tableAlias, BR)
    ret['route'] += BR
    ret['route'] += BR
    ret['route'] += "api_router.include_router(%s_router, prefix='%s', tags=['%s管理'])" % \
                    (tableInfo.tableName, tableInfo.apiPath, tableInfo.tableTitle)

    # ~~ Entity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    db = MySQLDB()
    db.connect(dbOptions)
    sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '%s' AND TABLE_SCHEMA = '%s'" % (tableInfo.tableName, DBNAME)
    print(sql)
    fields = db.getAll(sql)
    db.close()

    ret['Entity'] += "# -*- coding:utf-8 -*-" + BR
    ret['Entity'] += "from pydantic import Field, BaseModel" + BR
    ret['Entity'] += "from sqlalchemy import Column, Integer, String, DateTime,Enum, DECIMAL, Float, TEXT" + BR
    ret['Entity'] += "from core.curd import Base, TimestampMixin" + BR
    ret['Entity'] += BR
    ret['Entity'] += "class %s(Base, TimestampMixin):%s" % (CLASS_NAME, BR)
    ret['Entity'] += SPACE + '"""' + BR
    ret['Entity'] += SPACE + tableInfo.tableTitle + '表' + BR
    ret['Entity'] += SPACE + '"""' + BR
    ret['Entity'] += SPACE + "__tablename__ = '%s'%s" % (tableInfo.tableName, BR)
    ret['Entity'] += BR
    for f in fields:
        field = {
            "columnName": f['COLUMN_NAME'],
            "columnComment": f['COLUMN_COMMENT'],
            "columnType": f['COLUMN_TYPE'],
            "dataType": f['DATA_TYPE'],
            "isInsert": "0" if f['COLUMN_NAME'] in ['tenant_id', 'version', 'deleted', 'updater', 'creator', 'update_time','create_time', 'create_by'] else "1",
            "isEdit":   "0" if f['COLUMN_NAME'] in ['tenant_id', 'version', 'deleted', 'updater', 'creator', 'update_time','create_time', 'create_by', 'remark'] else "1",
            "isList":   "0" if f['COLUMN_NAME'] in ['tenant_id', 'version', 'deleted', 'updater', 'creator', 'create_time', 'update_time', 'create_by', 'update_by', 'remark'] else "1",
            "isQuery":  "0" if f['COLUMN_KEY'] == 'PRI' or f['COLUMN_NAME'] in ['tenant_id', 'version', 'deleted', 'updater', 'creator', 'create_time', 'update_time', 'create_by', 'update_by', 'remark'] else "1",
            "isRequired": "1",
            "htmlType": "input",
            "dictType": "",
            "regx": "",
            "pk": "1" if f['COLUMN_KEY'] == 'PRI' else "0",
            "queryType": "EQ",
            "defaulValue": "0" if f['DATA_TYPE'] in ['int', 'bigint', 'tinyint', 'float', 'double', 'decimal'] else "",
            "alias": f['COLUMN_NAME']
            #.split("_")[0] + "".join(
            #    map(lambda x: x[0].upper() + x[1:], f['COLUMN_NAME'].split("_")[1:]))
        }
        ret['fields'].append(field)
        if f['COLUMN_NAME'] in ['create_time', 'update_time', 'create_by', 'update_by', 'remark']:
            continue
        dataType = ""
        ext = ""
        if f['DATA_TYPE'] in ['bigint', 'tinyint', 'int']:
            dataType = 'Integer'
        elif f['DATA_TYPE'] in ['text', 'longtext']:
            dataType = 'TEXT'
        elif f['DATA_TYPE'] in ['varchar', 'char']:
            dataType = f['COLUMN_TYPE'].replace('varchar', 'String').replace('char', 'String')
        elif f['DATA_TYPE'] == 'datetime':
            dataType = 'DateTime'
        elif f['DATA_TYPE'] in ['float', 'double']:
            dataType = 'Float'
        elif f['DATA_TYPE'] == 'enum':
            dataType = f['COLUMN_TYPE'].replace('enum', 'Enum')
        elif f['DATA_TYPE'] == 'decimal':
            dataType = f['COLUMN_TYPE'].replace('decimal', 'DECIMAL')
        if f['IS_NULLABLE'] == 'NO':
            ext += ", nullable=False "
        if f['EXTRA'] == 'auto_increment':
            ext += ", autoincrement=True "
        if f['COLUMN_KEY'] == 'PRI':
            PRI = f['COLUMN_NAME']
            ext += ", primary_key=True "
        ret['Entity'] += "%s%s = Column( %s %s, comment='%s') %s" % (
            SPACE, f['COLUMN_NAME'], dataType, ext, f['COLUMN_COMMENT'], BR)
    ret['Entity'] += BR

    json_path = settings.RUNTIME_PATH + "/cache/table/" + CLASS_NAME + ".json"

    if deploy == 0:
        if os.path.exists(json_path):
            tableInfo.fields = json.load(open(json_path, 'r', encoding="utf8"))
            ret['fields'] = tableInfo.fields

    CreateModel = "class Create%s(BaseModel):%s" % (CLASS_NAME, BR)
    UpdateModel = "class Update%s(BaseModel):%s" % (CLASS_NAME, BR)

    for v in tableInfo.fields:
        if v['dataType'] in ['varchar', 'text', 'longtext', 'char']:
            tp = 'str'
        elif v['dataType'] in ['int', 'bigint', 'tinyint']:
            tp = 'int'
        elif v['dataType'] in ['float', 'double', 'decimal']:
            tp = 'float'
        elif v['dataType'] in ['datetime']:
            tp = 'datetime'
        else:
            tp = '???'

        if v['isRequired'] == '0':
            if v['dataType'] == 'datetime':
                tp = 'Union[date, datetime, None, EmptyStrToNone]'
            else:
                tp = 'Union[%s, None]' % tp
        ext = ""

        if v['dataType'] in ['varchar', 'char']:
            m = re.findall(r'\d+', v['columnType'])
            ext = ", max_length= %s" % m[0]

        if v['isRequired'] != '1' and v['dataType'] != 'datetime':
            ext += ", default='%s'" % v['defaulValue']

        if v['isInsert'] == '1':
            CreateModel += SPACE + "%s:%s  =  Field(comment='%s' %s)" % (v['columnName'], tp, v['columnComment'], ext) + BR
        if v['isEdit'] == '1':
            UpdateModel += SPACE + "%s:%s  =  Field(comment='%s' %s)" % (v['columnName'], tp, v['columnComment'], ext) + BR

    ret['Entity'] += CreateModel + BR
    ret['Entity'] += UpdateModel + BR


    # ~~ Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ret['Service'] += "# -*- coding:utf-8 -*-" + BR
    ret['Service'] += "from fastapi import Request" + BR
    ret['Service'] += "from core.curd import IBaseService" + BR
    ret['Service'] += "from app.dao.%s import %sDao %s" % ( moduleName2, CLASS_NAME, BR)
    ret['Service'] += "from app.entity.%s import %s %s" % (moduleName2,  CLASS_NAME, BR)
    ret['Service'] += BR
    ret['Service'] += 'class %sService(IBaseService):%s' % (CLASS_NAME, BR)
    ret['Service'] += BR
    ret['Service'] += BR
    ret['Service'] += SPACE + '@classmethod' + BR
    ret['Service'] += SPACE + 'async def instance(cls, req: Request):' + BR
    ret['Service'] += SPACE * 2 + 'inst = cls()' + BR
    ret['Service'] += SPACE * 2 + 'inst.dao = await %sDao.instance(req)' % (CLASS_NAME) + BR
    ret['Service'] += SPACE * 2 + 'return inst' + BR

    # ~~ Dao ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ret['Dao'] += "# -*- coding:utf-8 -*-" + BR
    ret['Dao'] += 'from fastapi import Request' + BR
    ret['Dao'] += 'from app.entity.%s import %s %s' % (tableInfo.moduleName, CLASS_NAME, BR)
    ret['Dao'] += 'from core.curd import CRUDBase, IBaseDao' + BR
    ret['Dao'] += 'from sqlalchemy.ext.asyncio import AsyncEngine' + BR
    ret['Dao'] += '' + BR
    ret['Dao'] += '' + BR
    ret['Dao'] += 'class %sDao(IBaseDao): %s' % (CLASS_NAME, BR)
    ret['Dao'] += SPACE + '"""' + BR
    ret['Dao'] += SPACE + '%s模块服务层 %s' % (tableInfo.tableTitle, BR)
    ret['Dao'] += SPACE + '"""' + BR
    ret['Dao'] += '' + BR
    ret['Dao'] += '' + BR
    ret['Dao'] += SPACE + 'def __init__(self, req: Request):' + BR
    ret['Dao'] += SPACE * 2 + 'self.db_session = req.app.state.db_async_session' + BR
    ret['Dao'] += SPACE * 2 + 'self.base = CRUDBase(%s) %s' % (CLASS_NAME, BR)
    ret['Dao'] += '' + BR
    ret['Dao'] += '' + BR
    ret['Dao'] += SPACE + '@classmethod' + BR
    ret['Dao'] += SPACE + 'async def instance(cls, req: Request):' + BR
    ret['Dao'] += SPACE * 2 + 'return cls(req)' + BR

    # ~~ Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ret['Controller'] += "# -*- coding:utf-8 -*-" + BR
    ret['Controller'] += 'from fastapi import APIRouter,Request,Depends,Query' + BR
    ret['Controller'] += 'from typing import Union' + BR
    ret['Controller'] += 'from core.curd import IDList' + BR
    ret['Controller'] += 'from core.request import get_page_info, get_query_param' + BR
    ret['Controller'] += 'from core.response import tableinfo, success' + BR
    ret['Controller'] += 'from app.annotation import RequiresPermissions' + BR
    ret['Controller'] += 'from app.service.%s import %sService' % (moduleName2,CLASS_NAME) + BR
    ret['Controller'] += 'from app.entity.%s import Create%s, Update%s' % (moduleName2,CLASS_NAME,CLASS_NAME) + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += 'router = APIRouter()' + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.get('/get')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":detail", BR)
    ret['Controller'] += "async def test(ids:int=Query(), mService=Depends(%sService.instance)):%s" % (CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '获取%s详情' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "one = await mService.select_by_id(ids)" + BR
    ret['Controller'] += SPACE + "return success('OK', one)" + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.get('/list')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":list", BR)
    ret['Controller'] += "async def lists(req: Request, page=Depends(get_page_info), where=Depends(get_query_param) ,mService=Depends(%sService.instance)):%s" % (CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '获取%s列表' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "total, rows = await mService.select_page(page, where)" + BR
    ret['Controller'] += SPACE + "return tableinfo('OK', total, rows)" + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.post('/add')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":add", BR)
    ret['Controller'] += "async def add(post:Create%s, mService=Depends(%sService.instance)):%s" % (CLASS_NAME, CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '添加%s' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "one = await mService.insert(post.dict())" + BR
    ret['Controller'] += SPACE + "return success('OK', one)" + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.post('/edit')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":edit", BR)
    ret['Controller'] += "async def update(post:Update%s, mService=Depends(%sService.instance)):%s" % (CLASS_NAME, CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '编辑%s' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "one = await mService.update(post.dict())" + BR
    ret['Controller'] += SPACE + "return success('OK', one)" + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.post('/del')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":remove", BR)
    ret['Controller'] += "async def remove(ids:IDList, mService=Depends(%sService.instance)):%s" % (CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '删除%s' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "one = await mService.delete_batch_ids(ids.ids)" + BR
    ret['Controller'] += SPACE + "return success('OK')" + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.post('/import')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":import", BR)
    ret['Controller'] += "async def imports(req:Request, mService=Depends(%sService.instance)):%s" % (CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '导入%s列表' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "" + BR
    ret['Controller'] += SPACE + "return success('OK')" + BR
    ret['Controller'] += '' + BR
    ret['Controller'] += "@router.post('/export')" + BR
    ret['Controller'] += "@RequiresPermissions('%s')%s" % (":".join(tableInfo.apiPath[1:].split("/")) + ":export", BR)
    ret['Controller'] += "async def export(req:Request, mService=Depends(%sService.instance)):%s" % (CLASS_NAME, BR)
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE * 2 + '导出%s列表' % tableInfo.tableTitle + BR
    ret['Controller'] += SPACE + '"""' + BR
    ret['Controller'] += SPACE + "" + BR
    ret['Controller'] += SPACE + "return success('OK')" + BR
    ret['Controller'] += '' + BR

    # ~~ api.js ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ret['api.js'] += "import service from '@/utils/request'" + BR
    ret['api.js'] += '' + BR
    ret['api.js'] += 'export const get%sApiList = (type:string)=>{%s' % (CLASS_NAME, BR)
    ret['api.js'] += SPACE + "if(type=='add'){" + BR
    ret['api.js'] += SPACE * 2 + "return '%s/add'%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += SPACE + "}else if(type=='edit'){" + BR
    ret['api.js'] += SPACE * 2 + "return '%s/edit'%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += SPACE + "}else if(type=='delete'){" + BR
    ret['api.js'] += SPACE + ""
    ret['api.js'] += SPACE + "}else if(type=='list' || type=='page'){" + BR
    ret['api.js'] += SPACE * 2 + "return '%s/list'%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += SPACE + "}else if(type=='detail'){" + BR
    ret['api.js'] += SPACE * 2 + "return '%s/get'%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += SPACE + "}" + BR
    ret['api.js'] += '}' + BR
    ret['api.js'] += '' + BR
    ret['api.js'] += "export const use%sApi = (id: number) => {%s" % (CLASS_NAME, BR)
    ret['api.js'] += SPACE + "return service.get('%s/get?ids=' + id)%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += "}" + BR
    ret['api.js'] += '' + BR
    ret['api.js'] += "export const use%sSubmitApi = (dataForm: any) => {%s" % (CLASS_NAME, BR)
    ret['api.js'] += SPACE + "if (dataForm.id) {" + BR
    ret['api.js'] += SPACE * 2 + "return service.post('%s/edit', dataForm)%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += SPACE + "} else {" + BR
    ret['api.js'] += SPACE * 2 + "return service.post('%s/add', dataForm)%s" % (tableInfo.apiPath, BR)
    ret['api.js'] += SPACE + "}" + BR
    ret['api.js'] += "}" + BR
    ret['api.js'] += '' + BR

    # ~~ index.vue ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ret['index.vue'] += "<template>" + BR
    ret['index.vue'] += SPACE + '<el-card>' + BR
    ret['index.vue'] += SPACE * 2 + '<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList()">' + BR
    for f in ret['fields']:
        if f['isQuery']=='1':
            ret['index.vue'] += SPACE * 3 + '<el-form-item>' + BR
            if f['htmlType'] == 'select':
                ret['index.vue'] += SPACE * 4 + '<fast-select v-model="state.queryForm.%s" dict-type="%s" clearable placeholder="%s"></fast-select>' % (f['alias'], f['dictType'], f['columnComment']) + BR
            elif f['htmlType'] == 'textarea':
                ret['index.vue'] += SPACE * 4 + '<el-input type="textarea" v-model="state.queryForm.%s" placeholder="%s" clearable></el-input>' % (f['alias'], f['columnComment']) + BR
            else:
                ret['index.vue'] += SPACE * 4 + '<el-input v-model="state.queryForm.%s" placeholder="%s" clearable></el-input>' % (f['alias'], f['columnComment']) + BR
            ret['index.vue'] += SPACE * 3 + '</el-form-item>' + BR
    ret['index.vue'] += SPACE * 3 + '<el-form-item>' + BR
    ret['index.vue'] += SPACE * 4 + '<el-button @click="getDataList()">查询</el-button>' + BR
    ret['index.vue'] += SPACE * 3 + '</el-form-item>' + BR
    ret['index.vue'] += SPACE * 3 + '<el-form-item>' + BR
    ret['index.vue'] += SPACE * 4 + '<el-button v-auth="\'sys:post:save\'" type="primary" @click="addOrUpdateHandle()">新增</el-button>' + BR
    ret['index.vue'] += SPACE * 3 + '</el-form-item>' + BR
    ret['index.vue'] += SPACE * 3 + '<el-form-item>' + BR
    ret['index.vue'] += SPACE * 4 + '<el-button v-auth="\'sys:post:delete\'" type="danger" @click="deleteBatchHandle()">删除</el-button>' + BR
    ret['index.vue'] += SPACE * 3 + '</el-form-item>' + BR
    ret['index.vue'] += SPACE * 2 + '</el-form>' + BR
    ret['index.vue'] += SPACE * 2 + '<el-table v-loading="state.dataListLoading" :data="state.dataList" border style="width: 100%" @selection-change="selectionChangeHandle">' + BR
    ret['index.vue'] += SPACE * 3 + '<el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>' + BR
    for f in ret['fields']:
        if f['isList'] == '1':
            if f['htmlType'] == 'select':
                ret['index.vue'] += SPACE * 3 + '<fast-table-column prop="%s" label="%s" dict-type="%s"></fast-table-column>' % (f['alias'], f['columnComment'], f['dictType']) + BR
            else:
                ret['index.vue'] += SPACE * 3 + '<el-table-column prop="%s" label="%s" header-align="center" align="center"></el-table-column>' % (f['alias'], f['columnComment']) + BR
    ret['index.vue'] += SPACE * 3 + '<el-table-column label="操作" fixed="right" header-align="center" align="center" width="150">' + BR
    ret['index.vue'] += SPACE * 4 + '<template #default="scope">' + BR
    ret['index.vue'] += SPACE * 5 + '<el-button v-auth="\'sys:post:update\'" type="primary" link @click="addOrUpdateHandle(scope.row.id)">修改</el-button>' + BR
    ret['index.vue'] += SPACE * 5 + '<el-button v-auth="\'sys:post:delete\'" type="primary" link @click="deleteBatchHandle(scope.row.id)">删除</el-button>' + BR
    ret['index.vue'] += SPACE * 4 + '</template>' + BR
    ret['index.vue'] += SPACE * 3 + '</el-table-column>' + BR
    ret['index.vue'] += SPACE * 2 + '</el-table>' + BR
    ret['index.vue'] += SPACE * 2 + '<el-pagination' + BR
    ret['index.vue'] += SPACE * 3 + ':current-page="state.page"' + BR
    ret['index.vue'] += SPACE * 3 + ':page-sizes="state.pageSizes"' + BR
    ret['index.vue'] += SPACE * 3 + ':page-size="state.limit"' + BR
    ret['index.vue'] += SPACE * 3 + ':total="state.total"' + BR
    ret['index.vue'] += SPACE * 3 + 'layout="total, sizes, prev, pager, next, jumper"' + BR
    ret['index.vue'] += SPACE * 3 + '@size-change="sizeChangeHandle"' + BR
    ret['index.vue'] += SPACE * 3 + '@current-change="currentChangeHandle">' + BR
    ret['index.vue'] += SPACE * 2 + '</el-pagination>' + BR
    ret['index.vue'] += BR
    ret['index.vue'] += SPACE * 2 + '<!-- 弹窗, 新增 / 修改 -->' + BR
    ret['index.vue'] += SPACE * 2 + '<add-or-update ref="addOrUpdateRef" @refresh-data-list="getDataList"></add-or-update>' + BR
    ret['index.vue'] += SPACE + "</el-card>" + BR
    ret['index.vue'] += "</template>" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += '<script setup lang="ts" name="%sIndex">%s' % (CLASS_NAME, BR)
    ret['index.vue'] += "import { useCrud } from '@/hooks/curd'" + BR
    ret['index.vue'] += "import { reactive, ref } from 'vue'" + BR
    ret['index.vue'] += "import AddOrUpdate from './add-or-update.vue'" + BR
    ret['index.vue'] += "import { IHooksOptions } from '@/hooks/interface'" + BR
    ret['index.vue'] += "import { get%sApiList } from '@/api/%s/%s'" % (CLASS_NAME, tableInfo.moduleName,tableInfo.tableAlias) + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "const state: IHooksOptions = reactive({" + BR
    ret['index.vue'] += SPACE  + "dataListUrl: get%sApiList ('page')," % (CLASS_NAME) + BR
    ret['index.vue'] += SPACE  + "deleteUrl: get%sApiList ('delete')," % (CLASS_NAME) + BR
    ret['index.vue'] += SPACE  + "queryForm: {" + BR
    for f in ret['fields']:
        if f['isQuery'] == '1':
            ret['index.vue'] += '      %s:"",' % (f['alias']) + BR
    ret['index.vue'] += SPACE * 1 + "}" + BR
    ret['index.vue'] += "})" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "const addOrUpdateRef = ref()" + BR
    ret['index.vue'] += "const addOrUpdateHandle = (id?: number) => {" + BR
    ret['index.vue'] += SPACE  + "addOrUpdateRef.value.init(id)" + BR
    ret['index.vue'] += "}" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "const { getDataList, selectionChangeHandle, sizeChangeHandle, currentChangeHandle, deleteBatchHandle } = useCrud(state)" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "</script>" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "<style scoped lang='scss'>" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "</style>" + BR
    ret['index.vue'] += "" + BR
    ret['index.vue'] += "" + BR

    ret['add-or-update.vue'] += "<template>" + BR
    ret['add-or-update.vue'] += SPACE2 + '<el-dialog v-model="visible" :title="!dataForm.id ? %s : %s" :close-on-click-modal="false" draggable>%s' % ( "'新增'", "'修改'", BR )
    ret['add-or-update.vue'] += SPACE2 * 2 + '<el-form ref="dataFormRef" :model="dataForm" :rules="dataRules" label-width="80px" @keyup.enter="submitHandle()">' + BR
    for f in ret['fields']:
        if f['isEdit'] == '1':
            if f['htmlType'] == 'select':
                ret['add-or-update.vue'] += SPACE2 * 3 + '<el-form-item prop="%s" label="%s"> %s' % (f['alias'], f['columnComment'], BR)
                ret['add-or-update.vue'] += SPACE2 * 4 + '<fast-select v-model="dataForm.%s" dict-type="%s"></fast-select> %s' % (f['alias'], f['dictType'], BR)
                ret['add-or-update.vue'] += SPACE2 * 3 + '</el-form-item>' + BR
            else:
                ret['add-or-update.vue'] += SPACE2 * 3 + '<el-form-item prop="%s" label="%s"> %s' % (f['alias'], f['columnComment'], BR)
                ret['add-or-update.vue'] += SPACE2 * 4 + '<el-input v-model="dataForm.%s" placeholder="%s"></el-input> %s' % (f['alias'], f['columnComment'], BR)
                ret['add-or-update.vue'] += SPACE2 * 3 + '</el-form-item>' + BR
    ret['add-or-update.vue'] += SPACE2 * 2 + '</el-form>' + BR
    ret['add-or-update.vue'] += SPACE2 * 2 + '<template #footer>' + BR
    ret['add-or-update.vue'] += SPACE2 * 3 + '<el-button @click="visible = false">取消</el-button>' + BR
    ret['add-or-update.vue'] += SPACE2 * 3 + '<el-button type="primary" @click="submitHandle()">确定</el-button>' + BR
    ret['add-or-update.vue'] += SPACE2 * 2 + '</template>' + BR
    ret['add-or-update.vue'] += SPACE2 + '</el-dialog>' + BR
    ret['add-or-update.vue'] += "</template>" + BR
    ret['add-or-update.vue'] += "" + BR
    ret['add-or-update.vue'] += '<script setup lang="ts">' + BR
    ret['add-or-update.vue'] += "import { reactive, ref } from 'vue'" + BR
    ret['add-or-update.vue'] += "import { ElMessage } from 'element-plus/es'" + BR
    ret['add-or-update.vue'] += "import { use%sApi, use%sSubmitApi } from '@/api/%s/%s' %s" % (CLASS_NAME, CLASS_NAME, tableInfo.moduleName, tableInfo.tableAlias, BR)
    ret['add-or-update.vue'] += "" + BR
    ret['add-or-update.vue'] += "const emit = defineEmits(['refreshDataList'])" + BR
    ret['add-or-update.vue'] += "const visible = ref(false)" + BR
    ret['add-or-update.vue'] += "const dataFormRef = ref()" + BR
    ret['add-or-update.vue'] += "const dataForm = reactive({" + BR
    for f in ret['fields']:
        if f['isEdit'] == '1':
            ret['add-or-update.vue'] += SPACE + '%s : "",' % (f['alias']) + BR
    ret['add-or-update.vue'] += "})" + BR
    ret['add-or-update.vue'] += "const init = (id?: number) => {" + BR
    ret['add-or-update.vue'] += "  visible.value = true" + BR
    ret['add-or-update.vue'] += "  dataForm.id = ''" + BR
    ret['add-or-update.vue'] += "  // 重置表单数据" + BR
    ret['add-or-update.vue'] += "  if (dataFormRef.value) {" + BR
    ret['add-or-update.vue'] += "    dataFormRef.value.resetFields()" + BR
    ret['add-or-update.vue'] += "  }" + BR
    ret['add-or-update.vue'] += "  // id 存在则为修改" + BR
    ret['add-or-update.vue'] += "  if (id) {" + BR
    ret['add-or-update.vue'] += "    get%s(id)" % (CLASS_NAME) + BR
    ret['add-or-update.vue'] += "  }" + BR
    ret['add-or-update.vue'] += "}" + BR
    ret['add-or-update.vue'] += "" + BR
    ret['add-or-update.vue'] += "const get%s = (id: number) => {" % (CLASS_NAME) + BR
    ret['add-or-update.vue'] += "  use%sApi(id).then(res => {" % (CLASS_NAME) + BR
    ret['add-or-update.vue'] += "    Object.assign(dataForm, res.data)" + BR
    ret['add-or-update.vue'] += "  })" + BR
    ret['add-or-update.vue'] += "}" + BR
    ret['add-or-update.vue'] += "" + BR
    ret['add-or-update.vue'] += "const dataRules = ref({" + BR
    for f in ret['fields']:
        if f['isRequired'] == '1':
            ret['add-or-update.vue'] += "  %s: [{ required: true, message: '必填项不能为空', trigger: 'blur' }]," % (f['alias']) + BR
    ret['add-or-update.vue'] += "})" + BR
    ret['add-or-update.vue'] += "" + BR
    ret['add-or-update.vue'] += "// 表单提交" + BR
    ret['add-or-update.vue'] += "const submitHandle = () => {" + BR
    ret['add-or-update.vue'] += "  dataFormRef.value.validate((valid: boolean) => {" + BR
    ret['add-or-update.vue'] += "    if (!valid) {" + BR
    ret['add-or-update.vue'] += "      return false" + BR
    ret['add-or-update.vue'] += "    }" + BR
    ret['add-or-update.vue'] += "    use%sSubmitApi(dataForm).then(() => {" % (CLASS_NAME) + BR
    ret['add-or-update.vue'] += "      ElMessage.success({" + BR
    ret['add-or-update.vue'] += "        message: '操作成功'," + BR
    ret['add-or-update.vue'] += "        duration: 500," + BR
    ret['add-or-update.vue'] += "        onClose: () => {" + BR
    ret['add-or-update.vue'] += "          visible.value = false" + BR
    ret['add-or-update.vue'] += "          emit('refreshDataList')" + BR
    ret['add-or-update.vue'] += "        }" + BR
    ret['add-or-update.vue'] += "      })" + BR
    ret['add-or-update.vue'] += "    })" + BR
    ret['add-or-update.vue'] += "  })" + BR
    ret['add-or-update.vue'] += "}" + BR
    
    ret['add-or-update.vue'] += "" + BR
    ret['add-or-update.vue'] += "defineExpose({" + BR
    ret['add-or-update.vue'] += "	init" + BR
    ret['add-or-update.vue'] += "})" + BR
    ret['add-or-update.vue'] += "</script>" + BR

    ret['__ini__'] += "# controller" + BR
    ret['__ini__'] += "from .%s_controller import router as %s_router %s" % (tableInfo.tableAlias,tableInfo.tableAlias,BR)
    ret['__ini__'] += "" + BR
    ret['__ini__'] += "# service" + BR
    ret['__ini__'] += "from .%s_service import %sService %s" % (tableInfo.tableAlias, CLASS_NAME,BR)
    ret['__ini__'] += "" + BR
    ret['__ini__'] += "# dao" + BR
    ret['__ini__'] += "from .%s_dao import %sDao %s" % (tableInfo.tableAlias, CLASS_NAME,BR)
    ret['__ini__'] += "" + BR
    ret['__ini__'] += "# entity" + BR
    ret['__ini__'] += "from .%s import %s %s" % (tableInfo.tableAlias, CLASS_NAME,BR)
    ret['__ini__'] += "" + BR

    ret['files'] += "# route" + BR
    ret['files'] += settings.ROOT_PATH + "/route/app.py" + BR
    ret['files'] += BR
    ret['files'] += "# __init__.py" + BR
    ret['files'] += settings.APP_PATH + "/controller/%s/__init__.py" % (tableInfo.moduleName) + BR
    ret['files'] += settings.APP_PATH + "/dao/%s/__init__.py" % (tableInfo.moduleName) + BR
    ret['files'] += settings.APP_PATH + "/entity/%s/__init__.py" % (tableInfo.moduleName) + BR
    ret['files'] += settings.APP_PATH + "/service/%s/__init__.py" % (tableInfo.moduleName) + BR
    ret['files'] += BR
    ret['files'] += "# entity" + BR
    ret['files'] += settings.APP_PATH + "/entity/%s/%s.py" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR
    ret['files'] += "# dao" + BR
    ret['files'] += settings.APP_PATH + "/dao/%s/%s_dao.py" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR
    ret['files'] += "# service" + BR
    ret['files'] += settings.APP_PATH + "/service/%s/%s_service.py" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR
    ret['files'] += "# controller" + BR
    ret['files'] += settings.APP_PATH + "/controller/%s/%s_controller.py" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR

    frontend_root = os.path.dirname(settings.ROOT_PATH)
    ret['files'] += "# api.js" + BR
    ret['files'] += frontend_root + "/frontend/src/api/%s/%s.js" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR
    ret['files'] += "# index.vue" + BR
    ret['files'] += frontend_root + "/frontend/src/views/%s/%s/index.vue" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR
    ret['files'] += "# add-or-update.vue" + BR
    ret['files'] += frontend_root + "/frontend/src/views/%s/%s/add-or-update.vue" % (tableInfo.moduleName, tableInfo.tableAlias) + BR
    ret['files'] += BR

    if deploy == 1:
        ret['fields'] = tableInfo.fields
        print("部署代码"+json_path)

        if not os.path.exists(settings.APP_PATH + "/controller/%s/" % (tableInfo.moduleName)):
            os.mkdir(settings.APP_PATH + "/controller/%s" % (tableInfo.moduleName))

        if not os.path.exists(settings.APP_PATH + "/dao/%s/" % (tableInfo.moduleName)):
            os.mkdir(settings.APP_PATH + "/dao/%s" % (tableInfo.moduleName))

        if not os.path.exists(settings.APP_PATH + "/service/%s/" % (tableInfo.moduleName)):
            os.mkdir(settings.APP_PATH + "/service/%s" % (tableInfo.moduleName))

        if not os.path.exists(frontend_root + "/frontend/src/api/%s/" % (tableInfo.moduleName)):
            os.mkdir(frontend_root + "/frontend/src/api/%s" % (tableInfo.moduleName))

        if not os.path.exists(frontend_root + "/frontend/src/views/%s/" % (tableInfo.moduleName)):
            os.mkdir(frontend_root + "/frontend/src/views/%s" % (tableInfo.moduleName))

        if not os.path.exists(frontend_root + "/frontend/src/views/%s/%s/" % (tableInfo.moduleName, tableInfo.tableAlias)):
            os.mkdir(frontend_root + "/frontend/src/views/%s/%s" % (tableInfo.moduleName, tableInfo.tableAlias))


        CONTROLLER_PATH = settings.APP_PATH + "/controller/%s/%s_controller.py" % (tableInfo.moduleName, tableInfo.tableAlias)
        DAO_PATH = settings.APP_PATH + "/dao/%s/%s_dao.py" % (tableInfo.moduleName, tableInfo.tableAlias)
        SERVICE_PATH = settings.APP_PATH + "/service/%s/%s_service.py" % (tableInfo.moduleName, tableInfo.tableAlias)
        JS_PATH = frontend_root + "/frontend/src/api/%s/%s.ts" % (tableInfo.moduleName, tableInfo.tableAlias)
        INDEX_VUE_PATH = frontend_root + "/frontend/src/views/%s/%s/index.vue" % (tableInfo.moduleName, tableInfo.tableAlias)
        ADD_UPDATE_VUE_PATH = frontend_root + "/frontend/src/views/%s/%s/add-or-update.vue" % (tableInfo.moduleName, tableInfo.tableAlias)

        with open(CONTROLLER_PATH, "w", encoding="utf8") as f:
            f.write(ret['Controller'])

        with open(DAO_PATH, "w", encoding="utf8") as f:
            f.write(ret['Dao'])

        # with open(ENTITY_PATH, "w", encoding="utf8") as f:
        #     f.write(ret['Entity'])

        with open(SERVICE_PATH, "w", encoding="utf8") as f:
            f.write(ret['Service'])

        with open(JS_PATH, "w", encoding="utf8") as f:
            f.write(ret['api.js'])

        with open(INDEX_VUE_PATH, "w", encoding="utf8") as f:
            f.write(ret['index.vue'])

        with open(ADD_UPDATE_VUE_PATH, "w", encoding="utf8") as f:
            f.write(ret['add-or-update.vue'])

        json.dump(tableInfo.fields, open(json_path, 'w', encoding="utf8"), ensure_ascii=False)

    return success("OK", ret)


@router.get("/dict")
async def dict():
    DBNAME = "fastframework"
    db = MySQLDB()
    db.connect(dbOptions)
    fields = db.getAll("select dict_type as dictType, dict_name as dictName from sys_dict_type where 1")
    db.close()
    return success("OK", fields)
